#include "batchputcommand.h"

#include "filemanager/filemanager.h"

#include <iostream>
#include <sstream>
using namespace std;

#include <Poco/DOM/Document.h>
#include <Poco/DOM/DOMWriter.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
using Poco::XML::Document;
using Poco::XML::DOMWriter;
using Poco::XML::DOMParser;
using Poco::XML::Element;
using Poco::XML::NodeList;

#include <Poco/Exception.h>
using Poco::Exception;

namespace Commands {

	string const BatchPutCommand::NAME = "BatchPut";

	BatchPutCommand::BatchPutCommand(Files const& files, NodeData* data)
		: NodeCommand(data, true)
	{
		cout << "BatchPutCommand :: We are potentially sending " << files.size() << " files" << endl;
		for (Files::const_iterator it = files.begin(); it != files.end(); ++it)
		{
			cout << "BatchPutCommand :: Seeing if '" << it->first << "' exists ... ";
			if (it->second.exists())
			{
				cout << "It does! Adding..." << endl;
				_files[it->first] = it->second;
			} else {
				cout << "It doesn't! Not Adding..." << endl;
			}
		}
	}

	string BatchPutCommand::repr()
	{
		ostringstream out;
		DOMWriter w;
		w.writeNode(out, toXML());
		return out.str();
	}

	string BatchPutCommand::name()
	{
		return string(BatchPutCommand::NAME);
	}

	CommandCreator BatchPutCommand::creator()
	{
		return &BatchPutCommand::makeCommand;
	}

	Document* BatchPutCommand::toXML(Document* )
	{
		Document* doc = new Document();

		for (Files::iterator it = _files.begin(); it != _files.end(); ++it)
		{
			Element* file = doc->createElement("file");
			file->setAttribute("name", it->first);
			doc->appendChild(file);
		}

		return NodeCommand::toXML(doc);
	}

	Command* BatchPutCommand::makeCommand(string r, void* d)
	{
		NodeData* data = (NodeData*) d;
		Files files;
		DOMParser p;
		Document* doc = p.parseString(r);

		FileManager fmgr;
		
		cout << "BatchPutCommand :: makeCommand :: We have a doc? " << (doc ? "yes" : "no") << endl;
		NodeList* ff = doc->getElementsByTagName("file");
		cout << "BatchPutCommand :: makeCommand :: We have a nodelist? " << (ff ? "yes" : "no") << endl;
		for(unsigned long i = 0; i < ff->length(); ++i)
		{
			Element* e = (Element*) (ff->item(i));
			cout << "BatchPutCommand :: makeCommand :: We have a element? " << (e ? "yes" : "no") << endl;
			string filename = e->getAttribute("name");
			cout << "BatchPutCommand :: makeCommand :: filename :: " << filename << endl;
			try {
				File ffff =  fmgr.searchFile(filename);
				cout << "BatchPutCommand :: makeCommand :: We have a file? " << (ffff.exists() ? "yes" : "no") << endl;
				files[e->getAttribute("name")] = ffff;
			} catch (...) {
				cout << "BatchPutCommand :: makeCommand :: something went wrong" << endl;
			}
		}

		return new BatchPutCommand(files, data);
	}

	void BatchPutCommand::execute()
	{
		Files failers;
		Files winners;

		cout << "BatchPutCommand :: going to find files that don't need forwarding" << endl;

		for (Files::iterator it = _files.begin(); it != _files.end(); ++it)
		{
			UInt32 h = FileManager::hash(it->first);
			UInt32 p = prevId();
			UInt32 i = nodeId();
			cout << "BatchPutCommand :: Prev | hash | Node :: " << p << " | " << h << " | " << i << endl;
			if (p < h && h <= i)
			{
				cout << "BatchPutCommand :: Keeping file #1 :: (" << p << ", " << h << ", " << i << ")" << endl;
				failers[it->first] = it->second;
			} else if (p > i && ((h < p && h <= i) || (h > p && h > i)))
			{
				/*
				 * Some info on this mayor comparison:
				 *
				 * when at the ID wrap-around, previous
				 * comparison does not hold. If the file
				 * belongs in this interval, it is either
				 * larger than the node ID and the prev ID or
				 * it is smaller than both of them.
				 */
				cout << "BatchPutCommand :: Keeping file #2 :: (" << p << ", " << h << ", " << i << ")" << endl;
				failers[it->first] = it->second;
			} else 
			{
				cout << "BatchPutCommand :: Forwarding file :: (" << p << ", " << h << ", " << i << ")" << endl;
				winners[it->first] = it->second;
			}
		}

		cout << "BatchPutCommand :: " << failers.size() << " files stayed behind" << endl;

		if (winners.size() > 0)
		{
			_files = winners;
			for (Files::iterator it = _files.begin(); it != _files.end(); ++it)
			{
				cout << "BatchPutCommand :: Putting '" << it->first << "'" << endl;
				node()->put(it->first, it->second);
				cout << "BatchPutCommand :: Putted '" << it->first << "'" << endl;
			}

			cout << "BatchPutCommand :: Forwarding myself" << endl;
			master()->executeCommand(this);

			for (Files::iterator it = _files.begin(); it != _files.end(); ++it)
			{
				cout << "BatchPutCommand :: Deleting file '" << it->first << "'" << endl;
				it->second.remove();
				cout << "BatchPutCommand :: File deleted" << endl;
			}
		} else {
			setResult("OK");
		}

		cout << "BatchPutCommand :: Done!" << endl;
	}

}
